package com.mallcai.bigdata.ladon.service.report.node;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.mallcai.bigdata.ladon.dpl.vo.DataType;
import com.mallcai.bigdata.ladon.service.report.impl.ReportQueryParamHolder;
import com.mallcai.bigdata.ladon.service.report.impl.handler.support.ParamValueConverter;
import com.mallcai.bigdata.ladon.service.report.impl.handler.support.rule.IRuleOperator;
import com.mallcai.bigdata.ladon.service.report.impl.handler.support.rule.RuleOperatorFactory;
import com.mallcai.bigdata.ladon.vo.Pair;

import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.mallcai.bigdata.ladon.service.report.impl.handler.ReplaceDynamicSqlHandler.ARRAY_PATTERN;
import static com.mallcai.bigdata.ladon.service.report.node.ReportSqlNodeUtils.*;
import static com.mallcai.bigdata.ladon.utils.SqlUtils.SINGLE_PARAM_NAME_PATTERN;

/**
 * 动态报表sql节点,每一个都为<IF></IF>
 * @author feixy
 * @version 1.0
 * @date 2019-08-03 16:00
 */
public class DynamicReportSqlNode implements SingleReportSqlNode {

    private static final Pattern PATTERN = Pattern.compile("(</?(exp1|exp2|op|text|elsetext)>)", Pattern.CASE_INSENSITIVE);

    private ReportSqlNode matchNode = NullReportSqlNode.getInstance();

    private ReportSqlNode noneMatchNode = NullReportSqlNode.getInstance();

    private String op;

    private ExpressionHolder expressionHolder1;

    private ExpressionHolder expressionHolder2;

    public DynamicReportSqlNode(String sql) {
        init(sql);
    }

    private void init(String sql){
        Matcher matcher = PATTERN.matcher(sql);
        Stack<Pair<String,Integer>> tagStack = new Stack<>();
        while(matcher.find()){
            String tag = matcher.group(1);
            String tagName = matcher.group(2);
            int  matchStart = matcher.start();
            int  matchEnd = matcher.end();
            // 如果是开标签
            if (tag.indexOf("/") == -1){
                tagStack.push(new Pair<>(tag, matchStart));
            } else {
                if (tagStack.isEmpty()){
                    throw new SqlNodeParseException("动态sql语法错误,存在非法的%s标签, sql片段:%s",tag, sql);
                }
                Pair<String,Integer> pair =  tagStack.pop();
                if (tagStack.isEmpty()){
                    // 只有栈为空,才表示完全匹配完成
                    String value = sql.substring(pair.getRight()+tagName.length()+2, matchEnd-tagName.length()-3);
                    initProp(tagName, value);
                }
            }
        }
        if(!tagStack.isEmpty()){
            Pair<String, Integer> pop = tagStack.pop();
            throw new SqlNodeParseException("动态sql语法错误,存在非法的%s标签, sql片段:%s",pop.getLeft(), sql);
        }

    }

    private void initProp(String tagName, String value) {
        if (tagName.equalsIgnoreCase(EXP1_TAG_NAME)){
            this.expressionHolder1 = new ExpressionHolder(value);
        }
        if (tagName.equalsIgnoreCase(EXP2_TAG_NAME)){
            this.expressionHolder2 = new ExpressionHolder(value);
        }
        if (tagName.equalsIgnoreCase(OP_TAG_NAME)){
            this.op = value;
        }
        if (tagName.equalsIgnoreCase(TEXT_TAG_NAME)){
            this.matchNode = ReportSqlNodeFactory.create(value);
        }
        if (tagName.equalsIgnoreCase(ELSE_TEXT_TAG_NAME)){
            this.noneMatchNode = ReportSqlNodeFactory.create(value);
        }
    }

    private boolean test(Map<String, ReportQueryParamHolder> queryParams){
        Object value1 = this.expressionHolder1.value;
        Object value2 = this.expressionHolder2.value;
        DataType dataType = DataType.STRING;
        if(expressionHolder1.paramName != null && expressionHolder2.paramName != null){
            ReportQueryParamHolder holder1 = checkParamNotNull(queryParams.get(expressionHolder1.paramName),expressionHolder1.paramName);
            ReportQueryParamHolder holder2 = checkParamNotNull(queryParams.get(expressionHolder2.paramName), expressionHolder2.paramName);
            // 两个参数的数据类型不一样,直接返回false
            if (holder1.getDataType() != holder2.getDataType()){
                return false;
            }
            dataType = holder1.getDataType();
            value1 = ParamValueConverter.convertIfNecessary(holder1.getValue(), dataType);
            value2 = ParamValueConverter.convertIfNecessary(holder2.getValue(), dataType);
        } else if (expressionHolder1.paramName != null){
            ReportQueryParamHolder holder1 = checkParamNotNull(queryParams.get(expressionHolder1.paramName),expressionHolder1.paramName);
            dataType = holder1.getDataType();
            value1 = ParamValueConverter.convertIfNecessary(holder1.getValue(), dataType);
            value2 = ParamValueConverter.convert(expressionHolder2.value, dataType, expressionHolder2.isMulti);
        } else if (expressionHolder2.paramName != null){
            ReportQueryParamHolder holder2 = checkParamNotNull(queryParams.get(expressionHolder2.paramName),expressionHolder2.paramName);
            dataType = holder2.getDataType();
            value2 = ParamValueConverter.convertIfNecessary(holder2.getValue(), dataType);
            value1 = ParamValueConverter.convert(expressionHolder1.value, dataType, expressionHolder1.isMulti);
        }

        IRuleOperator ruleOperator = RuleOperatorFactory.getRuleOperator(op, dataType);
        return ruleOperator.test(value1, value2);
    }

    private <T> T checkParamNotNull(T obj, Object...args){
        if (obj == null){
            throw new BusinessException(String.format("非法的规则:参数[%s]未配置", args));
        }
        return obj;
    }

    @Override
    public String toSql(Map<String, ReportQueryParamHolder> params) {
        String ans = test(params) ? matchNode.toSql(params) : noneMatchNode.toSql(params);
        return ans == null ? "" : ans.trim();
    }



    static class ExpressionHolder{

        private String value;

        private String paramName;

        private boolean isMulti = false;

        public ExpressionHolder(String content) {
            if(content != null){
                content = content.trim();
                this.paramName = getParamName(content);
                if (this.paramName == null){
                    this.value = getRealValue(content);
                    this.isMulti = !value.equals(content);
                }
            }
        }

        /**
         * 从exp中抽取参数名称
         * @param exp
         * @return
         */
        private String getParamName(String exp){
            if (exp.startsWith(":")){
                return exp;
            }
            Matcher matcher = SINGLE_PARAM_NAME_PATTERN.matcher(exp);
            if (matcher.find()){
                return matcher.group(1).trim();
            }
            return null;
        }

        /**
         * 从exp中抽取实际的值,会将数组表示的语法([])去掉
         * @param exp
         * @return
         */
        private String getRealValue(String exp){
            Matcher matcher = ARRAY_PATTERN.matcher(exp);
            if (matcher.find()){
                return matcher.group(1).trim();
            }
            return exp;
        }

    }
}
